package com.example.springboottest.example.atomic;

import com.example.springboottest.example.SalesDemo;
import com.example.springboottest.util.Print;
import com.example.springboottest.util.ThreadUtil;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicStampedReference;

/**
 * 什么是ABA问题？举一个例子来说明。比如一个线程A从内存位置 M中取出V1，另一个线程B也取出V1。
 * 现在假设线程B进行了一些操作 之后将M位置的数据V1变成了V2，然后又在一些操作之后将V2变成了 V1。
 * 之后，线程A进行CAS操作，但是线程A发现M位置的数据仍然是 V1，然后线程A操作成功。尽管线程A的CAS操作成功，
 * 但是不代表这 个过程是没有问题的，线程A操作的数据V1可能已经不是之前的V1，而 是被线程B替换过的V1，这就是ABA问题。
 * <p>
 * 很多乐观锁的实现版本都是使用版本号（Version）方式来解决 ABA问题。
 */
public class AtomicABATest {
    public static void main(String[] args) {
        CountDownLatch latch = new CountDownLatch(2);

        AtomicStampedReference<Integer> atomicStampedRef =
                new AtomicStampedReference<Integer>(1, 0);

        ThreadUtil.getMixedTargetThreadPool().submit(new Runnable() {
            @Override
            public void run() {
                boolean success = false;
                int stamp = atomicStampedRef.getStamp();
                Print.tco("before sleep 500: value=" + atomicStampedRef.getReference()
                        + " stamp=" + atomicStampedRef.getStamp());

                //等待500毫秒
                SalesDemo.sleepMilliSeconds(1);
                success = atomicStampedRef.compareAndSet(1, 10,
                        stamp, stamp + 1);

                Print.tco("after sleep 500 cas 1: success=" + success
                        + " value=" + atomicStampedRef.getReference()
                        + " stamp=" + atomicStampedRef.getStamp());


                //增加标记值
                stamp++;
                success = atomicStampedRef.compareAndSet(10, 1,
                        stamp, stamp + 1);
                Print.tco("after  sleep 500 cas 2: success=" + success
                        + " value=" + atomicStampedRef.getReference()
                        + " stamp=" + atomicStampedRef.getStamp());

                latch.countDown();
            }
        });

        ThreadUtil.getMixedTargetThreadPool().submit(new Runnable() {
            @Override
            public void run() {
                boolean success = false;
                int stamp = atomicStampedRef.getStamp();
                // stamp = 0
                Print.tco("before sleep 1000: value=" + atomicStampedRef.getReference()
                        + " stamp=" + atomicStampedRef.getStamp());

                //等待1000毫秒
                SalesDemo.sleepMilliSeconds(1);
                //stamp = 1
                Print.tco("after sleep 1000: stamp = " + atomicStampedRef.getStamp());
                success = atomicStampedRef.compareAndSet(1, 20, stamp, stamp++);
                Print.tco("after cas 3 1000: success=" + success
                        + " value=" + atomicStampedRef.getReference()
                        + " stamp=" + atomicStampedRef.getStamp());
                latch.countDown();
            }
        });
        try {
            latch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
